package com.xuecheng.ucenter.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.xuecheng.ucenter.mapper.XcUserMapper;
import com.xuecheng.ucenter.mapper.XcUserRoleMapper;
import com.xuecheng.ucenter.model.dto.AuthParamsDto;
import com.xuecheng.ucenter.model.dto.XcUserExt;
import com.xuecheng.ucenter.model.po.XcUser;
import com.xuecheng.ucenter.model.po.XcUserRole;
import com.xuecheng.ucenter.service.AuthService;
import com.xuecheng.ucenter.service.WxAuthService;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;

import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.Map;
import java.util.UUID;

/**
 * 微信扫码认证
 */
@Service("wx_authService")
@RequiredArgsConstructor
public class WxAuthServiceImpl implements AuthService, WxAuthService {

    private final XcUserMapper xcUserMapper;

    private final RestTemplate restTemplate;

    private final XcUserRoleMapper xcUserRoleMapper;

    @Autowired
    private WxAuthServiceImpl currentProxy;

    @Value("${weixin.appid}")
    String appid;

    @Value("${weixin.secret}")
    String secret;

    @Override
    public XcUserExt execute(AuthParamsDto authParamsDto) {
        // 得到账号
        String username = authParamsDto.getUsername();

        // 查询数据库
        XcUser xcUser = xcUserMapper.selectOne(new LambdaQueryWrapper<XcUser>().eq(XcUser::getUsername, username));
        if (xcUser == null) {
            throw new RuntimeException("用户不存在");
        }

        XcUserExt xcUserExt = new XcUserExt();
        BeanUtils.copyProperties(xcUser, xcUserExt);

        return xcUserExt;
    }

    /**
     * 微信扫码登录，申请令牌。携带令牌查询用户信息，保持用户信息到数据库
     *
     * @param code 授权码
     * @return
     */
    @Override
    public XcUser xcAuth(String code) {
        // 申请令牌
        Map<String, String> accessTokenMap = getAccess_token(code);
        // 获取信息
        String accessToken = accessTokenMap.get("access_token");
        String openid = accessTokenMap.get("openid");

        // 携带令牌查询用户信息
        Map<String, String> userinfo = getUserinfo(accessToken, openid);


        // 保存用户信息到到数据库
        // 注意这里不能直接调用新增用户的方法 因为xcAuth是非事务的方法
        // 一个非事务的方法调用一个事务的方法会导致事务失效 解决办法就是 自己注入自己使用代理对象去调用
        return currentProxy.addWxUser(userinfo);
    }

    /**
     * 携带授权码申请令牌
     * {
     * "access_token":"ACCESS_TOKEN",
     * "expires_in":7200,
     * "refresh_token":"REFRESH_TOKEN",
     * "openid":"OPENID",
     * "scope":"SCOPE",
     * "unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL"
     * }
     *
     * @param code 授权码
     */
    private Map<String, String> getAccess_token(String code) {
        // 微信开放平台授权地址
        String wxUrl_template = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code";

        // 最终的请求路径  appid secret code 会替换在 wxUrl_template 中 %s的位置
        String url = String.format(wxUrl_template, appid, secret, code);

        // 远程调用url
        ResponseEntity<String> exchange = restTemplate.exchange(url, HttpMethod.POST, null, String.class);
        // 获取响应的结构
        String result = exchange.getBody();
        // 将result转成map
        Map<String, String> map = JSON.parseObject(result, Map.class);
        return map;
    }

    /**
     * 携带令牌查询用户信息
     * {
     * "openid":"OPENID",
     * "nickname":"NICKNAME",
     * "sex":1,
     * "province":"PROVINCE",
     * "city":"CITY",
     * "country":"COUNTRY",
     * "headimgurl": "https://thirdwx.qlogo.cn/mmopen/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4eMsv84eavHiaiceqxibJxCfHe/0",
     * "privilege":[
     * "PRIVILEGE1",
     * "PRIVILEGE2"
     * ],
     * "unionid": " o6_bmasdasdsad6_2sgVt7hMZOPfL"
     * }
     */
    private Map<String, String> getUserinfo(String access_token, String openid) {
        // 微信开放平台授权地址
        String wxUrl_template = "https://api.weixin.qq.com/sns/userinfo?access_token=%s&openid=%s";

        // 最终的请求路径
        String url = String.format(wxUrl_template, access_token, openid);

        // 远程调用url
        ResponseEntity<String> exchange = restTemplate.exchange(url, HttpMethod.GET, null, String.class);

        // 获取响应的结构 解决乱码问题
        String result = new String(exchange.getBody().getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);

        // 将result转成map
        Map<String, String> map = JSON.parseObject(result, Map.class);
        return map;
    }


    @Transactional
    public XcUser addWxUser(Map<String, String> userInfoMap) {
        // 获取unionid
        String unionid = userInfoMap.get("unionid");
        // 根据unionid查询用户信息
        XcUser xcUser = xcUserMapper.selectOne(new LambdaQueryWrapper<XcUser>().eq(XcUser::getWxUnionid, unionid));
        if (xcUser != null) {
            // 说明已经存在了，直接返回
            return xcUser;
        }

        // 向数据库中写入数据
        String userId = UUID.randomUUID().toString();
        xcUser = new XcUser();
        xcUser.setId(userId);
        xcUser.setWxUnionid(unionid);
        // 记录从微信得到的昵称
        xcUser.setNickname(userInfoMap.get("nickname").toString());
        xcUser.setUserpic(userInfoMap.get("headimgurl").toString());
        xcUser.setName(userInfoMap.get("nickname").toString());
        xcUser.setUsername(unionid);
        xcUser.setPassword(unionid);
        xcUser.setUtype("101001"); // 学生类型
        xcUser.setStatus("1"); // 用户状态
        xcUser.setCreateTime(LocalDateTime.now());
        xcUserMapper.insert(xcUser);

        // 向用户角色关系表写入数据
        XcUserRole xcUserRole = new XcUserRole();
        xcUserRole.setId(UUID.randomUUID().toString());
        xcUserRole.setUserId(userId);
        xcUserRole.setRoleId("17"); // 学生角色
        xcUserRoleMapper.insert(xcUserRole);
        return xcUser;
    }
}
